package com.tea.admin.modules.app.service;  // 定义这个类所在的包路径

// 导入MyBatis-Plus查询条件相关的类
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;  // 导入Lambda风格的查询包装器
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;  // 导入普通的查询包装器
import com.tea.admin.modules.app.dto.*;  // 导入所有与DTO(数据传输对象)相关的类
import com.tea.admin.modules.app.mapper.GoodsAdminMapper;  // 导入商品管理的Mapper接口
import com.tea.admin.modules.app.mapper.UserAdminMapper;  // 导入用户管理的Mapper接口
import com.tea.common.entity.app.OrderInfo;  // 导入订单信息实体类
import com.tea.admin.modules.app.mapper.OrderInfoAdminMapper;  // 导入订单信息管理的Mapper接口

import com.tea.common.entity.app.User;  // 导入用户实体类
import com.tea.common.entity.app.dto.GoodsDTO;  // 导入商品DTO类
import lombok.RequiredArgsConstructor;  // 导入Lombok的注解，用于自动生成带有必需参数的构造函数
import org.springframework.stereotype.Service;  // 导入Spring的Service注解

// 导入Java标准库的各种工具类
import java.time.DayOfWeek;  // 表示一周中的某一天的枚举
import java.time.LocalDateTime;  // 表示日期和时间的类
import java.time.LocalTime;  // 表示时间的类
import java.time.ZoneId;  // 表示时区ID的类
import java.util.*;  // 导入Java集合框架
import java.util.regex.Matcher;  // 正则表达式匹配结果
import java.util.regex.Pattern;  // 正则表达式模式
import java.util.stream.Collectors;  // Stream API的收集器

/**
 * 数据统计面板Service实现类
 */
@Service  // 标记这个类是一个Spring服务组件，会被Spring自动扫描并注册
@RequiredArgsConstructor  // 生成一个包含final字段的构造函数，注入所需的依赖
public class DashboardServiceImpl implements DashboardService {  // 实现DashboardService接口

    private final OrderInfoAdminMapper orderInfoMapper;  // 订单信息数据访问对象，用于操作订单数据
    private final UserAdminMapper userAdminMapper;  // 用户数据访问对象，用于操作用户数据
    private final GoodsAdminMapper goodsAdminMapper;  // 商品数据访问对象，用于操作商品数据

    @Override  // 重写父接口中的方法
    public DashboardData getPanelData() {  // 获取仪表板数据的方法
        //  获取今日开始和结束时间
        LocalDateTime now = LocalDateTime.now();  // 获取当前日期时间
        LocalDateTime todayStart = now.with(LocalTime.MIN);  // 设置为今天的开始时间(00:00:00)
        LocalDateTime todayEnd = now.with(LocalTime.MAX);  // 设置为今天的结束时间(23:59:59.999999999)
        //  查询今日订单
        // 获取今日所有「已完成」订单
        List<OrderInfo> todayOrders = getOrdersByTimeRange(todayStart, todayEnd);  // 调用自定义方法获取今天的订单列表

        int todaySales = 0;  // 初始化今日销量为0
        Integer memberCount = userAdminMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getStatus, 1));  // 查询状态为1(正常)的用户数量
        double revenue = 0.0;  // 初始化营收为0
        for (OrderInfo todayOrder : todayOrders) {  // 遍历今日所有订单
            revenue += (double) todayOrder.getPayPrice() / 100;     // 数据库存储单位为分，转换为元后累加到营收中
            todaySales += todayOrder.getGoodsTotalNum();        // 累加每笔订单的商品总数到今日销量
        }
        // 今日订单总数
        int orders = todayOrders.size();  // 计算今日订单数量

        // 创建面板组数据对象，使用建造者模式设置各个字段
        PanelGroupData panelGroupData = PanelGroupData.builder()
                .todaySales(todaySales)  // 设置今日销量
                .memberCount(memberCount)  // 设置会员数量
                .revenue(revenue)  // 设置营收
                .orders(orders).build();  // 设置订单数量并构建对象

        // 获取各种图表数据
        LineChartData lineChartData = getLineChartData();  // 调用方法获取折线图数据
        RadarChartData radarChartData = getRadarChartData();  // 调用方法获取雷达图数据
        List<PieChartItem> pieChartData = getPieChartData();  // 调用方法获取饼图数据
        BarChartData barChartData = getBarChartData();  // 调用方法获取柱状图数据
        
        // 打包所有数据返回前端
        // 使用建造者模式创建并返回仪表板数据对象
        return DashboardData.builder()
                .panelGroup(panelGroupData)  // 设置面板组数据
                .lineChartData(lineChartData)  // 设置折线图数据
                .radarChartData(radarChartData)  // 设置雷达图数据
                .pieChartData(pieChartData)  // 设置饼图数据
                .barChartData(barChartData).build();  // 设置柱状图数据并构建对象

    }

    /**
     * 根据时间范围查询订单
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 订单列表
     */
    private List<OrderInfo> getOrdersByTimeRange(LocalDateTime start, LocalDateTime end) {  // 定义一个根据时间范围查询订单的私有方法
        return orderInfoMapper.selectList(  // 使用MyBatis-Plus的selectList方法查询订单列表
                new LambdaQueryWrapper<OrderInfo>()  // 创建Lambda风格的查询条件包装器
                        .ge(OrderInfo::getCreateTime, Date.from(start.atZone(ZoneId.systemDefault()).toInstant()))  // 创建时间大于等于开始时间，将LocalDateTime转换为Date类型
                        .le(OrderInfo::getCreateTime, Date.from(end.atZone(ZoneId.systemDefault()).toInstant()))  // 创建时间小于等于结束时间，将LocalDateTime转换为Date类型
                        .eq(OrderInfo::getOrderStatus, "已完成") // 只统计已完成的订单
        );
    }

    /**
     * 获取折线图数据 - 按周统计
     *
     * @return LineChartData 包含所有类型的折线图数据
     */
    private LineChartData getLineChartData() {  // 定义获取折线图数据的私有方法
        LineChartData lineChartData = new LineChartData();  // 创建一个折线图数据对象

        // 设置销量数据
        lineChartData.setNewVisitis(calculateSalesData());  // 调用方法计算销量数据并设置到对象中

        // 设置会员数据
        lineChartData.setMessages(calculateMemberData());  // 调用方法计算会员数据并设置到对象中

        // 设置营业额数据
        lineChartData.setPurchases(calculateRevenueData());  // 调用方法计算营业额数据并设置到对象中

        // 设置订单数据
        lineChartData.setShoppings(calculateOrderData());  // 调用方法计算订单数据并设置到对象中

        return lineChartData;  // 返回完整的折线图数据对象
    }

    /**
     * 计算销量数据
     */
    private ChartSeries calculateSalesData() {  // 定义计算销量数据的私有方法
        ChartSeries series = new ChartSeries();  // 创建一个图表系列对象

        // 固定的计划销量值
        List<Integer> expectedData = Arrays.asList(100, 120, 161, 134, 105, 160, 165);  // 定义预期的周一到周日的销量数据

        // 实际销量值
        List<Integer> actualData = new ArrayList<>();  // 创建一个空列表存储实际销量数据

        // 获取本周一到周日的日期范围
        LocalDateTime now = LocalDateTime.now();  // 获取当前日期时间
        LocalDateTime weekStart = now.with(DayOfWeek.MONDAY).with(LocalTime.MIN);  // 获取本周一的00:00:00

        // 计算每天的实际销量
        for (int i = 0; i < 7; i++) {  // 循环处理周一到周日
            LocalDateTime dayStart = weekStart.plusDays(i).with(LocalTime.MIN);  // 获取当天的开始时间
            LocalDateTime dayEnd = weekStart.plusDays(i).with(LocalTime.MAX);  // 获取当天的结束时间

            // 如果日期超过今天，使用计划值作为实际值
            if (dayStart.isAfter(now)) {  // 如果当天还没到
                actualData.add(expectedData.get(i));  // 添加预期销量作为实际销量
                continue;  // 继续下一天的处理
            }

            // 查询该日期的所有订单
            List<OrderInfo> orders = getOrdersByTimeRange(dayStart, dayEnd);  // 调用方法获取当天的订单列表

            // 统计总销量(杯数)
            int daySales = 0;  // 初始化当天销量为0
            for (OrderInfo order : orders) {  // 遍历当天的所有订单
                daySales += order.getGoodsTotalNum();  // 累加订单中的商品总数
            }

            actualData.add(daySales);  // 添加当天的实际销量到列表中
        }

        series.setExpectedData(expectedData);  // 设置预期销量数据到系列对象
        series.setActualData(actualData);  // 设置实际销量数据到系列对象
        return series;  // 返回完整的销量数据系列
    }

    /**
     * 计算会员数据
     */
    private ChartSeries calculateMemberData() {  // 定义计算会员数据的私有方法
        ChartSeries series = new ChartSeries();  // 创建一个图表系列对象

        // 固定的计划会员活跃数
        List<Integer> expectedData = Arrays.asList(200, 192, 120, 144, 160, 130, 140);  // 定义预期的周一到周日的会员活跃数

        // 实际会员活跃数
        List<Integer> actualData = new ArrayList<>();  // 创建一个空列表存储实际会员活跃数

        // 获取本周一到周日的日期范围
        LocalDateTime now = LocalDateTime.now();  // 获取当前日期时间
        LocalDateTime weekStart = now.with(DayOfWeek.MONDAY).with(LocalTime.MIN);  // 获取本周一的00:00:00

        // 计算每天的实际会员活跃数
        for (int i = 0; i < 7; i++) {  // 循环处理周一到周日
            LocalDateTime dayStart = weekStart.plusDays(i).with(LocalTime.MIN);  // 获取当天的开始时间

            // 如果日期超过今天，使用计划值作为实际值
            if (dayStart.isAfter(now)) {  // 如果当天还没到
                actualData.add(expectedData.get(i));  // 添加预期会员活跃数作为实际值
                continue;  // 继续下一天的处理
            }

            // 统计下单用户数(活跃会员数)
            int activeMemberCount = userAdminMapper.selectCount(new LambdaQueryWrapper<User>()
                    .eq(User::getStatus, 1));  // 查询状态为1(正常)的用户数量作为活跃会员数

            actualData.add(activeMemberCount);  // 添加活跃会员数到列表中
        }

        series.setExpectedData(expectedData);  // 设置预期会员活跃数到系列对象
        series.setActualData(actualData);  // 设置实际会员活跃数到系列对象
        return series;  // 返回完整的会员数据系列
    }

    /**
     * 计算营业额数据
     */
    private ChartSeries calculateRevenueData() {  // 定义计算营业额数据的私有方法
        ChartSeries series = new ChartSeries();  // 创建一个图表系列对象

        // 固定的计划营业额
        List<Integer> expectedData = Arrays.asList(80, 100, 121, 104, 105, 90, 100);  // 定义预期的周一到周日的营业额数据

        // 实际营业额
        List<Integer> actualData = new ArrayList<>();  // 创建一个空列表存储实际营业额数据

        // 获取本周一到周日的日期范围
        LocalDateTime now = LocalDateTime.now();  // 获取当前日期时间
        LocalDateTime weekStart = now.with(DayOfWeek.MONDAY).with(LocalTime.MIN);  // 获取本周一的00:00:00

        // 计算每天的实际营业额
        for (int i = 0; i < 7; i++) {  // 循环处理周一到周日
            LocalDateTime dayStart = weekStart.plusDays(i).with(LocalTime.MIN);  // 获取当天的开始时间
            LocalDateTime dayEnd = weekStart.plusDays(i).with(LocalTime.MAX);  // 获取当天的结束时间

            // 如果日期超过今天，使用计划值作为实际值
            if (dayStart.isAfter(now)) {  // 如果当天还没到
                actualData.add(expectedData.get(i));  // 添加预期营业额作为实际值
                continue;  // 继续下一天的处理
            }

            // 查询该日期的所有订单
            List<OrderInfo> orders = getOrdersByTimeRange(dayStart, dayEnd);  // 调用方法获取当天的订单列表

            // 统计总营业额(以百元为单位)
            double dayRevenue = 0.0;  // 初始化当天营业额为0
            for (OrderInfo order : orders) {  // 遍历当天的所有订单
                dayRevenue += (double) order.getPayPrice() / 100;  // 将订单支付金额(单位:分)转换为元，累加到当天营业额中
            }

            actualData.add((int) dayRevenue);  // 将当天营业额转换为整数并添加到列表中
        }

        series.setExpectedData(expectedData);  // 设置预期营业额数据到系列对象
        series.setActualData(actualData);  // 设置实际营业额数据到系列对象
        return series;  // 返回完整的营业额数据系列
    }

    /**
     * 计算订单数据
     */
    private ChartSeries calculateOrderData() {  // 定义计算订单数据的私有方法
        ChartSeries series = new ChartSeries();  // 创建一个图表系列对象

        // 固定的计划订单数
        List<Integer> expectedData = Arrays.asList(130, 140, 141, 142, 145, 150, 160);  // 定义预期的周一到周日的订单数据

        // 实际订单数
        List<Integer> actualData = new ArrayList<>();  // 创建一个空列表存储实际订单数据

        // 获取本周一到周日的日期范围
        LocalDateTime now = LocalDateTime.now();  // 获取当前日期时间
        LocalDateTime weekStart = now.with(DayOfWeek.MONDAY).with(LocalTime.MIN);  // 获取本周一的00:00:00

        // 计算每天的实际订单数
        for (int i = 0; i < 7; i++) {  // 循环处理周一到周日
            LocalDateTime dayStart = weekStart.plusDays(i).with(LocalTime.MIN);  // 获取当天的开始时间
            LocalDateTime dayEnd = weekStart.plusDays(i).with(LocalTime.MAX);  // 获取当天的结束时间

            // 如果日期超过今天，使用计划值作为实际值
            if (dayStart.isAfter(now)) {  // 如果当天还没到
                actualData.add(expectedData.get(i));  // 添加预期订单数作为实际值
                continue;  // 继续下一天的处理
            }

            // 查询该日期的所有订单
            List<OrderInfo> orders = getOrdersByTimeRange(dayStart, dayEnd);  // 调用方法获取当天的订单列表

            // 统计订单数
            int orderCount = orders.size();  // 计算订单数量

            actualData.add(orderCount);  // 添加当天的订单数到列表中
        }

        series.setExpectedData(expectedData);  // 设置预期订单数据到系列对象
        series.setActualData(actualData);  // 设置实际订单数据到系列对象
        return series;  // 返回完整的订单数据系列
    }

    public RadarChartData getRadarChartData() {  // 定义获取雷达图数据的公共方法
        RadarChartData radarChartData = new RadarChartData();  // 创建一个雷达图数据对象

        // 1. 设置雷达图指标 - 去掉会员占比，新增外卖占比
        List<RadarIndicator> indicators = Arrays.asList(  // 创建雷达图指标列表
                new RadarIndicator("销量", 100),  // 销量指标，最大值为100
                new RadarIndicator("销售额", 100),  // 销售额指标，最大值为100
                new RadarIndicator("订单量", 100),  // 订单量指标，最大值为100
                new RadarIndicator("客单价", 100),  // 客单价指标，最大值为100
                new RadarIndicator("外卖占比", 100),  // 外卖占比指标，最大值为100
                new RadarIndicator("高峰表现", 100)  // 高峰表现指标，最大值为100
        );
        radarChartData.setIndicator(indicators);  // 设置指标列表到雷达图数据对象

        // 2. 获取近30天的订单数据
        Calendar endCal = Calendar.getInstance();  // 获取当前日期作为结束日期
        Calendar startCal = Calendar.getInstance();  // 创建开始日期的日历对象
        startCal.add(Calendar.DAY_OF_MONTH, -30);  // 开始日期为当前日期前30天

        Date endTime = endCal.getTime();  // 获取结束时间
        Date startTime = startCal.getTime();  // 获取开始时间

        List<OrderInfo> orders = orderInfoMapper.selectList(  // 查询订单列表
                new LambdaQueryWrapper<OrderInfo>()  // 创建Lambda风格的查询条件包装器
                        .ge(OrderInfo::getCreateTime, startTime)  // 创建时间大于等于开始时间
                        .le(OrderInfo::getCreateTime, endTime)  // 创建时间小于等于结束时间
                        .eq(OrderInfo::getOrderStatus, "已完成")); // 只查询已完成的订单

        // 3. 产品类型
        List<String> productTypes = Arrays.asList("奶茶系列", "果茶系列", "特调系列");  // 定义产品类型列表

        // 4. 统计不同类型的数据
        Map<String, Integer> salesByType = new HashMap<>();          // 销量 - 存储每种类型的销量数据
        Map<String, Integer> revenueByType = new HashMap<>();        // 销售额 - 存储每种类型的销售额数据
        Map<String, Integer> orderCountByType = new HashMap<>();     // 订单量 - 存储每种类型的订单数量数据
        Map<String, List<Integer>> pricesByType = new HashMap<>();   // 价格 - 存储每种类型的价格列表
        Map<String, Integer> takeawayOrdersByType = new HashMap<>(); // 外卖订单 - 存储每种类型的外卖订单数量
        Map<String, Integer> totalOrdersByType = new HashMap<>();    // 总订单 - 存储每种类型的总订单数量
        Map<String, Integer> peakSalesByType = new HashMap<>();      // 高峰销量 - 存储每种类型在高峰期的销量

        // 正则表达式解析商品信息 - 匹配"商品名[规格] * 数量"的格式
        Pattern pattern = Pattern.compile("([^\\[]*)(\\[.*])?\\s*\\*\\s*(\\d+)");  // 正则表达式模式

        // 5. 遍历订单收集数据
        for (OrderInfo order : orders) {  // 遍历所有订单
            // 商品信息在goodsDetail字段中
            String goodsDetail = order.getGoodsPreview();  // 获取订单的商品预览信息

            if (goodsDetail == null || goodsDetail.isEmpty()) continue;  // 如果商品详情为空，跳过此订单

            // 判断是否高峰时段(14:00-18:00)
            Calendar calendar = Calendar.getInstance();  // 创建日历对象
            calendar.setTime(order.getCreateTime());  // 设置为订单创建时间
            int hour = calendar.get(Calendar.HOUR_OF_DAY);  // 获取小时数(24小时制)
            boolean isPeakHour = (hour >= 14 && hour <= 18);  // 判断是否是高峰时段(下午2点到6点)

            // 判断是否外卖订单
            boolean isTakeaway = "外卖配送".equals(order.getTakeType());  // 判断订单取货类型是否为"外卖配送"

            // 解析商品详情 - 根据分隔符拆分多个商品
            String[] goodsItems = goodsDetail.split(",");  // 按逗号分隔获取多个商品

            // 记录此订单中包含的产品类型
            Set<String> typesInOrder = new HashSet<>();  // 创建一个集合记录订单中的产品类型(去重)

            for (String goodsItem : goodsItems) {  // 遍历订单中的每个商品
                Matcher matcher = pattern.matcher(goodsItem);  // 用正则表达式匹配商品信息
                if (matcher.find()) {  // 如果匹配成功
                    String productName = matcher.group(1).trim();  // 获取商品名称并去除首尾空格
                    int quantity = Integer.parseInt(matcher.group(3));  // 获取商品数量并转换为整数

                    // 根据商品名称判断类型
                    String productType = categorizeProduct(productName);  // 调用方法分类商品
                    if (!productTypes.contains(productType)) continue;  // 如果不是我们关注的类型，跳过此商品

                    // 记录此订单包含的产品类型
                    typesInOrder.add(productType);  // 将产品类型添加到集合中(自动去重)

                    // 累加销量
                    salesByType.put(productType, salesByType.getOrDefault(productType, 0) + quantity);  // 将商品数量累加到对应类型的销量中

                    // 累加销售额 (单位是分)
                    int price = order.getPayPrice() / order.getGoodsTotalNum(); // 平均每杯价格
                    int itemRevenue = price * quantity;  // 计算此商品的销售额
                    revenueByType.put(productType, revenueByType.getOrDefault(productType, 0) + itemRevenue);  // 累加到对应类型的销售额中

                    // 记录价格
                    if (!pricesByType.containsKey(productType)) {  // 如果价格Map中没有此类型
                        pricesByType.put(productType, new ArrayList<>());  // 创建一个新的价格列表
                    }
                    pricesByType.get(productType).add(price);  // 将价格添加到对应类型的价格列表中

                    // 高峰时段销量
                    if (isPeakHour) {  // 如果是高峰时段
                        peakSalesByType.put(productType, peakSalesByType.getOrDefault(productType, 0) + quantity);  // 将销量累加到高峰销量中
                    }
                }
            }

            // 更新订单计数
            for (String type : typesInOrder) {  // 遍历订单中包含的所有产品类型
                orderCountByType.put(type, orderCountByType.getOrDefault(type, 0) + 1);  // 将订单数量加1
                totalOrdersByType.put(type, totalOrdersByType.getOrDefault(type, 0) + 1);  // 将总订单数量加1

                // 外卖订单
                if (isTakeaway) {  // 如果是外卖订单
                    takeawayOrdersByType.put(type, takeawayOrdersByType.getOrDefault(type, 0) + 1);  // 将外卖订单数量加1
                }
            }
        }

        // 6. 找出各维度的最大值
        int maxSales = salesByType.values().stream().max(Integer::compare).orElse(1);  // 找出最大销量，如果没有则默认为1
        int maxRevenue = revenueByType.values().stream().max(Integer::compare).orElse(1);  // 找出最大销售额，如果没有则默认为1
        int maxOrderCount = orderCountByType.values().stream().max(Integer::compare).orElse(1);  // 找出最大订单量，如果没有则默认为1

        // 计算平均价格的最大值
        int maxAvgPrice = 0;  // 初始化最大平均价格为0
        for (Map.Entry<String, List<Integer>> entry : pricesByType.entrySet()) {  // 遍历每种类型的价格列表
            if (!entry.getValue().isEmpty()) {  // 如果价格列表不为空
                int avgPrice = (int) entry.getValue().stream().mapToInt(Integer::intValue).average().orElse(0);  // 计算平均价格
                maxAvgPrice = Math.max(maxAvgPrice, avgPrice);  // 更新最大平均价格
            }
        }
        if (maxAvgPrice == 0) maxAvgPrice = 1; // 避免除零，如果最大平均价格为0，设为1

        int maxPeakSales = peakSalesByType.values().stream().max(Integer::compare).orElse(1);  // 找出最大高峰销量，如果没有则默认为1

        // 7. 为每种产品类型创建雷达图数据
        List<RadarSeries> seriesList = new ArrayList<>();  // 创建雷达图系列列表

        for (String type : productTypes) {  // 遍历每种产品类型
            // 计算该类型的6个维度值
            List<Integer> values = new ArrayList<>();  // 创建存储维度值的列表

            // 销量
            int sales = salesByType.getOrDefault(type, 0);  // 获取该类型的销量，如果没有则为0
            values.add(normalizeValue(sales, maxSales));  // 将销量标准化到0-100范围并添加到列表

            // 销售额
            int revenue = revenueByType.getOrDefault(type, 0);  // 获取该类型的销售额，如果没有则为0
            values.add(normalizeValue(revenue, maxRevenue));  // 将销售额标准化到0-100范围并添加到列表

            // 订单量
            int orderCount = orderCountByType.getOrDefault(type, 0);  // 获取该类型的订单量，如果没有则为0
            values.add(normalizeValue(orderCount, maxOrderCount));  // 将订单量标准化到0-100范围并添加到列表

            // 客单价
            List<Integer> prices = pricesByType.getOrDefault(type, Collections.emptyList());  // 获取该类型的价格列表，如果没有则为空列表
            int avgPrice = prices.isEmpty() ? 0 :
                    (int) prices.stream().mapToInt(Integer::intValue).average().orElse(0);  // 计算平均价格
            values.add(normalizeValue(avgPrice, maxAvgPrice));  // 将平均价格标准化到0-100范围并添加到列表

            // 外卖占比
            int takeawayOrders = takeawayOrdersByType.getOrDefault(type, 0);  // 获取该类型的外卖订单数量，如果没有则为0
            int totalOrders = totalOrdersByType.getOrDefault(type, 0);  // 获取该类型的总订单数量，如果没有则为0
            double takeawayRate = totalOrders > 0 ? (double) takeawayOrders / totalOrders : 0;  // 计算外卖占比，避免除零
            values.add((int) (takeawayRate * 100));  // 将外卖占比转换为百分比(0-100)并添加到列表

            // 高峰表现
            int peakSales = peakSalesByType.getOrDefault(type, 0);  // 获取该类型的高峰销量，如果没有则为0
            values.add(normalizeValue(peakSales, maxPeakSales));  // 将高峰销量标准化到0-100范围并添加到列表

            // 添加到系列列表
            RadarSeries series = new RadarSeries();  // 创建一个雷达图系列对象
            series.setName(type);  // 设置系列名称为产品类型
            series.setValue(values);  // 设置系列的值为计算出的维度值列表
            seriesList.add(series);  // 将系列添加到列表中
        }

        radarChartData.setSeries(seriesList);  // 设置系列列表到雷达图数据对象
        return radarChartData;  // 返回完整的雷达图数据对象
    }

    /**
     * 将值标准化到0-100范围
     */
    private int normalizeValue(int value, int maxValue) {  // 定义将值标准化到0-100范围的私有方法
        if (maxValue <= 0) return 0;  // 如果最大值小于等于0，返回0(避免除零)
        return (int) ((double) value / maxValue * 100);  // 计算并返回标准化后的值((值/最大值)*100)
    }

    /**
     * 根据商品名称判断所属类型
     */
    private String categorizeProduct(String productName) {  // 定义根据商品名称判断所属类型的私有方法
        if (productName == null) return "其他";  // 如果商品名称为null，返回"其他"

        // 根据商品名称判断类型
        if (productName.contains("奶茶") || productName.contains("珍珠") ||
                productName.contains("波霸") || productName.contains("乌龙")) {  // 如果包含这些关键词，属于奶茶系列
            return "奶茶系列";
        } else if (productName.contains("果茶") || productName.contains("水果") ||
                productName.contains("柠檬") || productName.contains("柚子")) {  // 如果包含这些关键词，属于果茶系列
            return "果茶系列";
        } else if (productName.contains("芝士") || productName.contains("奶盖") ||
                productName.contains("特调") || productName.contains("茉香")) {  // 如果包含这些关键词，属于特调系列
            return "特调系列";
        } else {
            return "其他";  // 如果不属于上述任何类型，返回"其他"
        }
    }

    /**
     * 获取饼图数据 - 奶茶销售占比
     *
     * @return 饼图数据列表
     */
    public List<PieChartItem> getPieChartData() {  // 定义获取饼图数据的公共方法
        // 1. 获取近30天的订单数据
        Calendar endCal = Calendar.getInstance();  // 获取当前日期作为结束日期
        Calendar startCal = Calendar.getInstance();  // 创建开始日期的日历对象
        startCal.add(Calendar.DAY_OF_MONTH, -30);  // 开始日期为当前日期前30天

        Date endTime = endCal.getTime();  // 获取结束时间
        Date startTime = startCal.getTime();  // 获取开始时间

        List<OrderInfo> orders = orderInfoMapper.selectList(  // 查询订单列表
                new LambdaQueryWrapper<OrderInfo>()  // 创建Lambda风格的查询条件包装器
                        .ge(OrderInfo::getCreateTime, startTime)  // 创建时间大于等于开始时间
                        .le(OrderInfo::getCreateTime, endTime)  // 创建时间小于等于结束时间
                        .eq(OrderInfo::getOrderStatus, "已完成"));  // 只查询已完成的订单

        // 2. 统计不同奶茶品类的销量
        Map<String, Integer> salesByCategory = new HashMap<>();  // 创建一个Map存储每个类别的销量

        // 正则表达式解析商品信息 - 匹配"商品名[规格] * 数量"的格式
        Pattern pattern = Pattern.compile("([^\\[]*)(\\[.*?])?\\s*\\*\\s*(\\d+)");  // 正则表达式模式

        // 3. 遍历订单收集数据
        for (OrderInfo order : orders) {  // 遍历所有订单
            // 商品信息在goodsDetail字段中
            String goodsDetail = order.getGoodsPreview();  // 获取订单的商品预览信息
            if (goodsDetail == null || goodsDetail.isEmpty()) continue;  // 如果商品详情为空，跳过此订单

            // 解析商品详情 - 根据逗号拆分多个商品
            String[] goodsItems = goodsDetail.split(",");  // 按逗号分隔获取多个商品

            for (String goodsItem : goodsItems) {  // 遍历订单中的每个商品
                Matcher matcher = pattern.matcher(goodsItem);  // 用正则表达式匹配商品信息
                if (matcher.find()) {  // 如果匹配成功
                    String productName = matcher.group(1).trim();  // 获取商品名称并去除首尾空格
                    int quantity = Integer.parseInt(matcher.group(3));  // 获取商品数量并转换为整数

                    // 根据商品名称获取商品信息
                    GoodsDTO goodsDTO = getGoodsByName(productName);  // 调用方法查询商品信息

                    // 获取商品类别名称
                    String category;
                    if (goodsDTO != null && goodsDTO.getGoodsCategoryName() != null) {  // 如果找到了商品信息且类别名称不为空
                        category = goodsDTO.getGoodsCategoryName();  // 使用数据库中的类别名称
                    } else {
                        // 如果找不到商品，则设为"其他"
                        category = "其他";  // 默认为"其他"类别
                    }

                    // 累加销量
                    salesByCategory.put(category, salesByCategory.getOrDefault(category, 0) + quantity);  // 将商品数量累加到对应类别的销量中
                }
            }
        }

        // 4. 转换为饼图数据格式
        List<PieChartItem> pieChartData = new ArrayList<>();  // 创建饼图数据列表

        // 移除销量过低的类别（可选）
        final int MIN_SALES = 10; // 最小展示阈值，低于此销量的类别将被过滤

        // 对类别按销量排序
        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(salesByCategory.entrySet());  // 创建类别条目列表
        sortedEntries.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); // 从高到低排序，销量高的排在前面

        // 最多展示前10个类别（可根据需要调整）
        int maxCategories = Math.min(sortedEntries.size(), 10);  // 计算要展示的类别数量，最多10个

        for (int i = 0; i < maxCategories; i++) {  // 遍历排序后的前maxCategories个类别
            Map.Entry<String, Integer> entry = sortedEntries.get(i);  // 获取当前类别条目
            String category = entry.getKey();  // 获取类别名称
            int value = entry.getValue();  // 获取类别销量

            // 可选：跳过销量过低的类别
            if (value < MIN_SALES) continue;  // 如果销量低于最小阈值，跳过此类别

            PieChartItem item = new PieChartItem();  // 创建一个饼图项目对象
            item.setName(category);  // 设置项目名称为类别名称
            item.setValue(value);  // 设置项目值为类别销量
            pieChartData.add(item);  // 将项目添加到饼图数据列表中
        }

        // 如果类别太多，可以将剩余类别合并为"其他"
        if (sortedEntries.size() > maxCategories) {  // 如果总类别数超过了我们要展示的数量
            int otherValue = 0;  // 初始化"其他"类别的销量为0
            for (int i = maxCategories; i < sortedEntries.size(); i++) {  // 遍历剩余的类别
                otherValue += sortedEntries.get(i).getValue();  // 累加销量到"其他"类别
            }

            if (otherValue > 0) {  // 如果"其他"类别的销量大于0
                PieChartItem otherItem = new PieChartItem();  // 创建一个新的饼图项目对象
                otherItem.setName("其他");  // 设置项目名称为"其他"
                otherItem.setValue(otherValue);  // 设置项目值为累计的销量
                pieChartData.add(otherItem);  // 将"其他"项目添加到饼图数据列表中
            }
        }

        return pieChartData;  // 返回完整的饼图数据列表
    }

    /**
     * 根据商品名称查询商品信息
     */
    private GoodsDTO getGoodsByName(String productName) {  // 定义根据商品名称查询商品信息的私有方法
        // 实际情况中可能需要模糊查询或其他匹配逻辑
        return goodsAdminMapper.selectOne(  // 使用MyBatis-Plus的selectOne方法查询单个商品
                new QueryWrapper<GoodsDTO>()  // 创建查询条件包装器
                        .like("name", productName)  // name字段中包含指定的商品名称
                        .last("LIMIT 1"));  // 限制只返回一条结果
    }

    /**
     * 获取柱状图数据 - 奶茶周销量统计（显示销量前三的类别）
     *
     * @return 柱状图数据
     */
    public BarChartData getBarChartData() {  // 定义获取柱状图数据的公共方法
        BarChartData barChartData = new BarChartData();  // 创建一个柱状图数据对象

        // 1. 设置x轴数据 - 周一到周日
        List<String> days = Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日");  // 创建日期列表
        barChartData.setDays(days);  // 设置日期列表到柱状图数据对象

        // 2. 获取本周的开始和结束日期
        Calendar now = Calendar.getInstance();  // 获取当前日期
        Calendar weekStart = (Calendar) now.clone();  // 复制当前日期作为周开始日期

        // 将日期调整到本周一
        weekStart.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);  // 设置为周一
        weekStart.set(Calendar.HOUR_OF_DAY, 0);  // 设置时为0(即00点)
        weekStart.set(Calendar.MINUTE, 0);  // 设置分为0
        weekStart.set(Calendar.SECOND, 0);  // 设置秒为0

        Calendar weekEnd = (Calendar) weekStart.clone();  // 复制周开始日期作为周结束日期
        weekEnd.add(Calendar.DAY_OF_WEEK, 6);  // 增加6天到周开始日期(即周日)
        weekEnd.set(Calendar.HOUR_OF_DAY, 23);  // 设置时为23
        weekEnd.set(Calendar.MINUTE, 59);  // 设置分为59
        weekEnd.set(Calendar.SECOND, 59);  // 设置秒为59

        // 3. 查询本周订单
        List<OrderInfo> orders = orderInfoMapper.selectList(  // 查询订单列表
                new LambdaQueryWrapper<OrderInfo>()  // 创建Lambda风格的查询条件包装器
                        .ge(OrderInfo::getCreateTime, weekStart.getTime())  // 创建时间大于等于周开始时间
                        .le(OrderInfo::getCreateTime, weekEnd.getTime())  // 创建时间小于等于周结束时间
                        .eq(OrderInfo::getOrderStatus, "已完成"));  // 只查询已完成的订单

        // 4. 统计各类别总销量
        Map<String, Integer> totalSalesByCategory = new HashMap<>();  // 创建一个Map存储每个类别的总销量
        Map<String, int[]> salesByDayAndCategory = new HashMap<>();  // 创建一个Map存储每个类别每天的销量

        // 正则表达式解析商品信息 - 匹配"商品名[规格] * 数量"的格式
        Pattern pattern = Pattern.compile("([^\\[]*)(\\[.*?])?\\s*\\*\\s*(\\d+)");  // 正则表达式模式

        // 5. 统计各类别的销量
        for (OrderInfo order : orders) {  // 遍历所有订单
            // 获取订单的星期几(0-6对应周一到周日)
            Calendar orderDate = Calendar.getInstance();  // 创建日历对象
            orderDate.setTime(order.getCreateTime());  // 设置为订单创建时间
            int dayOfWeek = orderDate.get(Calendar.DAY_OF_WEEK);  // 获取星期几(1-7对应周日到周六)

            // 将Calendar.DAY_OF_WEEK转换为数组索引(0-6)
            int dayIndex;
            if (dayOfWeek == Calendar.SUNDAY) {  // 如果是周日
                dayIndex = 6; // 周日是索引6
            } else {
                dayIndex = dayOfWeek - 2; // 转换为0-5的索引(周一到周六)
            }

            // 商品信息
            String goodsDetail = order.getGoodsPreview();  // 获取订单的商品预览信息
            if (goodsDetail == null || goodsDetail.isEmpty()) continue;  // 如果商品详情为空，跳过此订单

            // 解析商品详情 - 根据逗号拆分多个商品
            String[] goodsItems = goodsDetail.split(",");  // 按逗号分隔获取多个商品

            for (String goodsItem : goodsItems) {  // 遍历订单中的每个商品
                Matcher matcher = pattern.matcher(goodsItem);  // 用正则表达式匹配商品信息
                if (matcher.find()) {  // 如果匹配成功
                    String productName = matcher.group(1).trim();  // 获取商品名称并去除首尾空格
                    int quantity = Integer.parseInt(matcher.group(3));  // 获取商品数量并转换为整数

                    // 从数据库查询商品类别
                    GoodsDTO goodsDTO = goodsAdminMapper.selectOne(  // 使用MyBatis-Plus的selectOne方法查询单个商品
                            new QueryWrapper<GoodsDTO>()  // 创建查询条件包装器
                                    .like("name", productName)  // name字段中包含指定的商品名称
                                    .last("LIMIT 1"));  // 限制只返回一条结果

                    // 获取商品类别，如果找不到则归为"其他"
                    String category = "其他";  // 默认类别为"其他"
                    if (goodsDTO != null && goodsDTO.getGoodsCategoryName() != null) {  // 如果找到了商品信息且类别名称不为空
                        category = goodsDTO.getGoodsCategoryName();  // 使用数据库中的类别名称
                    }

                    // 累加类别总销量
                    totalSalesByCategory.put(category, totalSalesByCategory.getOrDefault(category, 0) + quantity);  // 将商品数量累加到对应类别的总销量中

                    // 初始化类别的每日销量数组(如果不存在)
                    if (!salesByDayAndCategory.containsKey(category)) {  // 如果销量Map中没有此类别
                        salesByDayAndCategory.put(category, new int[7]);  // 创建一个长度为7的数组(对应周一到周日)
                    }

                    // 累加对应日期的销量
                    salesByDayAndCategory.get(category)[dayIndex] += quantity;  // 将商品数量累加到对应类别和日期的销量中
                }
            }
        }

        //  确定销量前三的类别
        List<Map.Entry<String, Integer>> sortedCategories = new ArrayList<>(totalSalesByCategory.entrySet());  // 创建类别条目列表
        sortedCategories.sort((e1, e2) -> e2.getValue().compareTo(e1.getValue())); // 从高到低排序，销量高的排在前面

        //  创建柱状图数据系列
        List<BarSeries> seriesList = new ArrayList<>();  // 创建柱状图系列列表

        // 获取前三名类别（或者全部，如果不足三个）
        int count = Math.min(3, sortedCategories.size());  // 计算要展示的类别数量，最多3个
        for (int i = 0; i < count; i++) {  // 遍历排序后的前count个类别
            String category = sortedCategories.get(i).getKey();  // 获取类别名称

            BarSeries series = new BarSeries();  // 创建一个柱状图系列对象
            series.setName(category);  // 设置系列名称为类别名称
            series.setData(Arrays.stream(salesByDayAndCategory.get(category)).boxed().collect(Collectors.toList()));  // 将int[]转换为List<Integer>并设置为系列数据
            seriesList.add(series);  // 将系列添加到列表中
        }

        barChartData.setSeries(seriesList);  // 设置系列列表到柱状图数据对象

        return barChartData;  // 返回完整的柱状图数据对象
    }
}
